import browser from "webextension-polyfill";
import { throttle } from 'lodash'
import dayjs from 'dayjs'

import {spotMarket, spotMarketCommon} from '@src/api/spotMarket'

import { DataSourceTypeByDateEnum, DataSourceTypeEnum } from '@src/globalData/enum'
import { AsyncRecursion, creatAsyncRecursion } from '@src/utils/asyncRecursion'
import { getTimeRangePoints, promisetTimeOut } from '@src/utils/common'
import { dataCategory } from '@src/dataFormatConversion/constant'
import { getAllNodeData } from '../options/views/BatchData/generate'
import { yunNaoSaveNodeData } from '../options/views/BatchData/generate'

import type { CreateOneSpotMarketReqParams } from './type'
import type { GetAllNodeDataParams } from '../options/views/BatchData/type'
import type { ReqType } from '@src/dataFormatConversion/type'
import { PrivateDataParams, startGetSpotMarketPrivateData } from "./spotMarketPrivateData";
import { saveOneWithoutCal } from "@src/dataFormatConversion/saveOneWithoutCal";
import { getAccountNodeInfo } from "@src/config/accountNode";


const getDataIntervalMinute = 15  // 每次获取数据的间隔时间  /分钟
// const getDataIntervalMinute = 1  // 每次获取数据的间隔时间  /分钟
const dTypeIntervalSecond = 5 // 不同D类型的间隔时间 /秒 
const everyReqIntervalSecond = 1 // 每个请求的间隔时间 /秒


/**
 * 固定时刻获取 120节点的数据
 * @param headers fetch 的headers
 * @returns 
 */
type NodeValues = DataSourceTypeEnum.ACT_NODE_PRICE_INFO | DataSourceTypeEnum.AHEAD_NODE_PRICE_INFO
const saveAllNodeData = async ({headers, nowStr}:{headers?:HeadersInit, nowStr:string}) => {
    /**
   * 【现货市场】-【现货交易】-【日前交易结果】-【一次日前节点电价查询】
   * 全网所有节点日前出清节点电价
   * D + 1
   * AHEAD_NODE_PRICE_INFO = "ahead_node_price_info"
   */
  /**
   * https://pmos.sd.sgcc.com.cn:18080/trade/main/home/changeDate.do?pdate=2024-03-19&_=1711692352927
   * 【现货市场】-【现货交易】-【实时交易结果】-【实时节点电价查询】
   * 全网所有节点实时出清节点电价
   * D - 1
   * ACT_NODE_PRICE_INFO = "act_node_price_info",
   */
  console.log(
    `%c 获取120节点数据`,
    'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px;  color: #fff',
  )

  try {
    const reqTypes:Record<NodeValues, string[]> = {
      [DataSourceTypeEnum.AHEAD_NODE_PRICE_INFO]: getTimeRangePoints([['19:00', '23:45']]),
      [DataSourceTypeEnum.ACT_NODE_PRICE_INFO]: ['09:00', '09:30', '10:00']
    }

    let reqType:NodeValues | undefined = undefined
    for(let key in reqTypes) {
      const timeList = reqTypes[key as NodeValues]
      if(timeList.includes(nowStr)) {
        reqType = key as NodeValues
      }
    }

    if(!reqType) return Promise.resolve()
  
    const dates:Partial<Record<DataSourceTypeEnum, string>> = {
      [DataSourceTypeEnum.AHEAD_NODE_PRICE_INFO]: dayjs().add(1, 'day').format('YYYY-MM-DD'),
      [DataSourceTypeEnum.ACT_NODE_PRICE_INFO]: dayjs().subtract(1, 'day').format('YYYY-MM-DD')
    }
  
    const ruleForm:GetAllNodeDataParams = {
      certainDate: dates[reqType] || '',
      reqType,
      uniqueCode:''
    }
  
    const nodeData = await getAllNodeData(ruleForm, headers, false)
    await yunNaoSaveNodeData(nodeData)
  } catch(e) {
    console.error(e)
    return Promise.resolve()
  }
}

/**
 * 补充获取 全网所有节点日前出清节点电价
 */
const replenishAheadNodeData = async ({headers, nowStr}:{headers?:HeadersInit, nowStr:string}) => {
    /**
   * 【现货市场】-【现货交易】-【日前交易结果】-【一次日前节点电价查询】
   * 全网所有节点日前出清节点电价
   * D + 1
   * AHEAD_NODE_PRICE_INFO = "ahead_node_price_info"
   */
  console.log(
    `%c 补充 获取120节点数据 全网所有节点日前出清节点电价D + 1`,
    'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px;  color: #fff',
  )

  try {
    if(!['06:00', '07:00', '07:30'].includes(nowStr)) return

    const ruleForm:GetAllNodeDataParams = {
      certainDate: dayjs().format('YYYY-MM-DD'),
      reqType: DataSourceTypeEnum.AHEAD_NODE_PRICE_INFO,
      uniqueCode:''
    }
  
    const nodeData = await getAllNodeData(ruleForm, headers, false)
    await yunNaoSaveNodeData(nodeData)
  } catch(e) {
    console.error(e)
    return Promise.resolve()
  }
}

/** 补充获取 d_date 滚动的实时数据, 解决大概在23：00 ~ 24：00 缺数据的问题 */
const replenishDdateData = async ({headers, nowStr}:{headers:HeadersInit, nowStr:string}) => {
  console.log(
    `%c 补充获取 d_date 滚动的实时数据`,
    'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px;  color: #fff',
  )
  
    try {
      if(!['00:30', '02:00', '07:00', '07:30', '07:45', '08:00', '08:15', '08:30', '09:00'].includes(nowStr)) return
    
      const params:CreateImmediateReqParams = {
        certainDate: dayjs().subtract(1, 'day').format('YYYY-MM-DD'),
        type: 'd_date',
        headers
      }
    
      await createImmediateReq(params)
      console.log('replenishDdateData 补充数据成功')
    } catch(e) {
      console.error(e)
      return Promise.resolve()
    }
}


/** 第二日补充获取 d_add 数据, 解决交易中心数据发布 */
const replenishDADDdateData = async ({headers, nowStr}:{headers:HeadersInit, nowStr:string}) => {
  console.log(
    `%c 补充获取 d_add 数据`,
    'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px;  color: #fff',
  )
  
    try {
      if(!getTimeRangePoints([['00:15', '02:00'], ['06:30', '09:15']]).includes(nowStr)) return
    
      const params:CreateImmediateReqParams = {
        certainDate: dayjs().format('YYYY-MM-DD'),
        type: 'd_add_date',
        headers
      }
    
      await createImmediateReq(params)
      console.log('replenishDADDdateData 补充数据成功')
    } catch(e) {
      console.error(e)
      return Promise.resolve()
    }
}

/** 增加第二日凌晨数据 */
// const replenishFutureData = async ({headers, nowStr}:{headers:HeadersInit, nowStr:string}) => {
//   console.log(
//     `%c 增加第二日凌晨数据`,
//     'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px;  color: #fff',
//   )
  
//     const pdate = dayjs().add(1, 'day').format('YYYY-MM-DD')

//     if(!getTimeRangePoints([['21:30', '23:45']]).includes(nowStr)) return Promise.resolve()
    
//     try {
//       // 设置数据日期
//       await spotMarketCommon?.sdSpotMarketChangeDate?.(pdate)

//       await (spotMarket[DataSourceTypeEnum.REALTIME_NODE_PRICE_INFO] && spotMarket[DataSourceTypeEnum.REALTIME_NODE_PRICE_INFO]?.({headers, pdate, unitid}, 2))
//     } catch(e) {
//       console.error(e)
//       return Promise.resolve()
//     }
// }

/** 第二天补 D+1 私有数据 */
const replenishPrivateDAddData = async ({headers, unitid, nowStr}:{headers:HeadersInit, unitid?:string, nowStr:string}) => {     
    console.log(
      `%c 第二天补 D+1 私有数据`,
      'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px;  color: #fff',
    )
    
      try {
        if(!getTimeRangePoints([['00:15', '02:00'], ['06:30', '09:15']]).includes(nowStr)) return
      
        const params:PrivateDataParams = {
          headers, unitid,
          certainDate: dayjs().format('YYYY-MM-DD'),
          dataDate: 1
        }
      
        await startGetSpotMarketPrivateData(params)
        console.log('replenishPrivateDAddData 补充数据成功')
      } catch(e) {
        console.error(e)
        return Promise.resolve()
      }
}

/** 部分接口需要 机组id 参数 */
let unitid:string | undefined = ''

const setUinitId = (id?:string) => {
  if(!id) return 
  unitid = id
}

/** 创建5min执行一次的定时请求的 记录timer */
const all5minTimeOutTimerList:Array<ReturnType<typeof setTimeout>> = []
/** 清除所有 5min执行一次的 timeOut */
const clearAll5minTimeOut = () => {
  all5minTimeOutTimerList.forEach(val => clearTimeout(val))
  all5minTimeOutTimerList.length = 0
}

/** 定时器 集合 */
const allIntervalTimerList:Array<ReturnType<typeof setInterval>> = []
/** 清除所有 interval */
const clearAllInterval = () => {
  allIntervalTimerList.forEach(val => clearInterval(val))
  allIntervalTimerList.length = 0
}

/** 创建所有类型的所有接口的定时请求的 记录timer */
const allTimeOutTimerList:Array<ReturnType<typeof setTimeout>> = []
/** 清除所有 timeOut */
const clearAllTimeOut = () => {
  clearAll5minTimeOut()
  allTimeOutTimerList.forEach(val => clearTimeout(val))
  allTimeOutTimerList.length = 0
}

/** 创建一个 现货市场的请求 */
const createOneSpotMarketReq = async ({headers, reqType, pdate, key}:CreateOneSpotMarketReqParams) => {
  try {
    await (spotMarket[key] && spotMarket[key]?.({headers, reqType, pdate, unitid}))
    return Promise.resolve()
  }catch(e) { 
    console.error(e)
    return Promise.reject(e)
  }
}

/** 每5min执行一次请求的数据 */
const loop5min = ({headers}:{headers:HeadersInit}) => {
  const {nodeName, nodeId} = getAccountNodeInfo()
  if(!nodeName || !nodeId) return

  clearAll5minTimeOut()

  const nextMin5 = req5minActionTime() + 20 * 1000
  const nextNextMin5 = nextMin5 + 5 * 60 * 1000

  const timer4 = setTimeout(() => {
    // 节点电价 (实时-临时 ) :DataSourceTypeEnum.REALTIME_NODE_PRICE_INFO
    reqOneCertainData({headers, unitid, key: DataSourceTypeEnum.REALTIME_NODE_PRICE_INFO})
  }, nextMin5)
  const timer9 = setTimeout(() => {
    // 节点电价 (实时-临时 ) :DataSourceTypeEnum.REALTIME_NODE_PRICE_INFO
    reqOneCertainData({headers, unitid, key: DataSourceTypeEnum.REALTIME_NODE_PRICE_INFO})
  }, nextNextMin5)
  console.log(`loop5min ${dayjs().format('HH:mm:ss')}, nextMin5:${nextMin5/1000}s, ${dayjs().add(nextMin5,'millisecond').format('HH:mm:ss')} nextNextMin5:${nextNextMin5/1000}s`)

  all5minTimeOutTimerList.push(timer4)
  all5minTimeOutTimerList.push(timer9)
}

/**
 * 获取数据code的 D 类型
 * @param key DataSourceTypeEnum
 * @returns DataSourceTypeByDateEnum
 */
  const getTypeByKey = (key:DataSourceTypeEnum):DataSourceTypeByDateEnum => {
    let dataDType:DataSourceTypeByDateEnum = DataSourceTypeByDateEnum.D_DATE

    for(let type in dataCategory) {
      if(dataCategory[type as DataSourceTypeByDateEnum].includes(key)) {
        dataDType = type as DataSourceTypeByDateEnum
        break
      }
    }
    return dataDType
  }

  /** 请求单项数据 */
  const reqOneCertainData = async ({headers, key, unitid, reqType}:Omit<CreateOneSpotMarketReqParams, 'pdate'>) => {
    console.log('reqOneCertainData',key, dayjs().format('YYYY-MM-DD HH:mm:ss'))
    try {
      const dType = reqType || getTypeByKey(key)
      const pdate = getPdate(dType)

      // 设置数据日期
      await spotMarketCommon?.sdSpotMarketChangeDate?.(pdate)
      await promisetTimeOut(100)

      const { data } = await (spotMarket[key] && spotMarket[key]?.({headers, pdate, unitid, calculateSaveData: false}))

      saveOneWithoutCal(key, data, pdate)
      return Promise.resolve()
    } catch(e) {
      console.error(e)
      return Promise.reject(e)
    }
  }

/** 获取需要设置的系统日期 */
export const getPdate = (type:ReqType) => {
  let pdate = dayjs().format('YYYY-MM-DD')
  switch(type) {
    case 'd_add_date':
      pdate = dayjs(pdate).add(1, 'day').format('YYYY-MM-DD')
      break;
      case 'd_date':
      break;
      case 'd_reduce_date':
        pdate = dayjs(pdate).subtract(1, 'day').format('YYYY-MM-DD')
        break;
      default:
        break;
  }
  return pdate
}

type CreateImmediateReqParams = {type:ReqType, headers:HeadersInit, certainDate?: string}
type CreateImmediateReq = {(params:CreateImmediateReqParams):Promise<void>}
/**
 * 根据类型 ReqType 创建对应类型的所有请求
 * @param param0 CreateImmediateReqParams
 */
const createImmediateReq:CreateImmediateReq = async ({type, headers, certainDate}) => {
  console.log(
    `%c 数据D类型 ${type}`,
    'background:pink ; padding: 1px; border-radius: 3px 0 0 3px;  color: #fff',
  )
  try {
    let pdate = getPdate(type)
    if(certainDate) pdate = dayjs(certainDate).format('YYYY-MM-DD')

    // 设置数据日期
    await spotMarketCommon?.sdSpotMarketChangeDate?.(pdate)

    // 获取对应类型的请求
    const reqList = Object.keys(spotMarket).filter((key) => dataCategory[type].includes(key as DataSourceTypeEnum)) as Array<DataSourceTypeEnum>

    // 单个请求的入参
    const reqParamsList:CreateOneSpotMarketReqParams[] = reqList.map((key) => ({headers, reqType:type, pdate, key}))

    // 请求对应ReqType的所有数据
    await creatAsyncRecursion(AsyncRecursion, {
      stepAsyncFnParamsList: reqParamsList,
      stepAsyncFn: createOneSpotMarketReq,
      stepCb: async () => await promisetTimeOut(everyReqIntervalSecond * 1000), // 每隔 发出请求，防反爬
      stepAsyncNum: 1,
      errBreak: false
    }).start()

  } catch(e) {
    console.error(e)
  }
}

type CreateAllImmediateReqParams = {headers:HeadersInit, certainDate?: string, reqTypeList?:ReqType[], id?:string}
type CreateAllImmediateReq = {(params:CreateAllImmediateReqParams):Promise<void>}
/**
 * 立即创建所有类型对应的所有的请求
 * @param headers 请求的headers
 */
export const createAllImmediateReq:CreateAllImmediateReq = async ({headers, reqTypeList = ['d_date', 'd_add_date', 'd_reduce_date'], certainDate, id}) => {
  console.log(
    `%c createAllImmediateReq ${import.meta.env.VITE_APP_ENV}`,
    'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px;  color: #fff',
  )
  setUinitId(id)
  try {
    console.log(
      `%c 是否获取公有数据 ${import.meta.env.VITE_APP_PUBLIC}`,
      'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px;  color: #fff',
    )
    clearAll5minTimeOut()

    const nowStr = dayjs().format('HH:mm')

    if(import.meta.env.VITE_APP_PUBLIC) {

      // 单个请求类型的入参
      const reqParamsList:CreateImmediateReqParams[] = reqTypeList.map((type) => ({headers, type, certainDate}))

      await creatAsyncRecursion(AsyncRecursion, {
        stepAsyncFnParamsList: reqParamsList,
        stepAsyncFn: createImmediateReq,
        stepCb: async () => await promisetTimeOut(dTypeIntervalSecond * 1000), // 每种类型隔, 防反爬和日期错误
        stepAsyncNum: 1,
        errBreak: false
      }).start()

      // 120 节点数据
      await saveAllNodeData({headers, nowStr})
      // 补充获取 全网所有节点日前出清节点电价
      await replenishAheadNodeData({headers, nowStr})

      // 凌晨补充 实时滚动的数据
      await replenishDdateData({headers, nowStr})
      
      // 增加第二日凌晨数据 (伪需求，交易中心实际不会提前发布第二日的数据)
      // await replenishFutureData({headers, nowStr})

      // 解决交易中心数据发布延迟，D+1 数据
      await replenishDADDdateData({headers, nowStr})

      // 事前监管 改为 D+1
      // await reqOneCertainData({headers, unitid, reqType: 'd_add_date', key: DataSourceTypeEnum.AHEAD_REGULATED_INFO})
    } else {
      const {nodeName, nodeId} = getAccountNodeInfo()
      if(nodeName && nodeId) {
        // 节点电价 (实时-临时 ) :DataSourceTypeEnum.REALTIME_NODE_PRICE_INFO
        await reqOneCertainData({headers, unitid, key: DataSourceTypeEnum.REALTIME_NODE_PRICE_INFO})
      }
    }
    
      // 私有的现货市场数据
      await startGetSpotMarketPrivateData({headers, unitid})

      // 第二天补 D+1 私有数据
      await replenishPrivateDAddData({headers, unitid, nowStr})

      //约每5min执行一次请求的数据
      loop5min({headers})

  } catch(e) {
    console.error(e)
  }
}

/** 计算距离第二次获取数据时间 */
export const reqActionTime = () => {
  // development production
  const appEnv = import.meta.env.VITE_APP_ENV

  let nextTime = 15;
  const cMinute = dayjs().minute()
  
  // 生产环境：每到 00、15、30、45 时刻获取数据
  // 测试环境：每到 07、22、37、52 时刻获取数据
  const reqTimePoint = /^dev/.test(appEnv) ? 7 : 15
  const reqPoints = Array.from({length:5}).map((val, index) => reqTimePoint + nextTime * index)

   for(let i = 0; i < reqPoints.length; i++) {
    const dTime = reqPoints[i] - cMinute
    if(dTime > 0 && dTime <= 15) {
      nextTime = dTime
      break;
    }
   }

   const cSecond = dayjs().second()

   nextTime = (nextTime * 60 - cSecond + 20) * 1000

   return nextTime
}

/** 计算距离第二次获取数据时间 每5min */
export const req5minActionTime = () => {
  const currentMinutes = dayjs().minute()
  const currentSeconds = dayjs().second()

  // 下一个分钟数能被 5 整除的分钟值
  const nextFiveMinutes = Math.ceil(currentMinutes / 5) * 5

  // 距离目标时间的差值（分钟和秒）
  const minuteDifference = nextFiveMinutes - currentMinutes
  const totalMillisecond = (minuteDifference * 60 - currentSeconds) * 1000

  // 处理跨小时情况
  if (minuteDifference === 0 && currentSeconds === 0) {
      return 0; // 当前时刻已是目标时刻
  }
  if (minuteDifference === 0) {
      return (5 * 60 - currentSeconds) * 1000; // 到下一个整点的秒数
  }

  return totalMillisecond;
}

/** 创建所有类型对应的所有的请求 的  请求间隔*/
const createAllIntervalReq = (headers:HeadersInit) => {
  // 距离下一个获取数据时刻的毫秒数
  const intervalActiontime = reqActionTime()

  console.log(
    `%c 当前时间: ${dayjs().format('YYYY-MM-DD HH:mm:ss')}`,
    'background:red ; padding: 1px; border-radius: 3px 0 0 3px;  color: black'
  )
  console.log(
    `%c 下一次获取数据时间: ${dayjs(new Date().getTime() + intervalActiontime).format('YYYY-MM-DD HH:mm:ss')}`,
    'background:pink ; padding: 1px; border-radius: 3px 0 0 3px;  color: black'
  )

  clearAllTimeOut()
  const timeOutTimer = setTimeout(() => {
    createAllImmediateReq({headers})
    createAllIntervalReq(headers)
  }, intervalActiontime)
  
  allTimeOutTimerList.push(timeOutTimer)
}


 /** 创建所有类型的所有接口的定时请求 */
 const createSpotMarketAllReqLoop = (headers:HeadersInit, id?:string) => {
  console.log(
    `%c createSpotMarketAllReqLoop ${import.meta.env.VITE_APP_ENV}`,
    'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px;  color: #fff',
  )
  setUinitId(id)

  // if(import.meta.env.VITE_APP_ENV === 'development') createAllImmediateReq({headers})

  createAllIntervalReq(headers)
}

export const createSpotMarketAllReqLoopThrottle = throttle(createSpotMarketAllReqLoop, 5000, {trailing:true, leading: false})


export const bindBackgroundMsg = () => {
  function handleMessage(request:any) {
    console.log('bindBackgroundMsg', request)
    if(!request) return
    const message = request?.message
    if(!['stopBackgroundAutoGetdata', 'startBackgroundAutoGetdata'].includes(message)) return

    return new Promise((resolve, reject) => {
      try {
        if(message === 'stopBackgroundAutoGetdata') {
          clearAllInterval()
          clearAllTimeOut()
          return resolve('stopBackgroundAutoGetdata success')
        }
        if(message === 'startBackgroundAutoGetdata') {
          createSpotMarketAllReqLoopThrottle(request.headers, request.unitid);
          return resolve('startBackgroundAutoGetdata success')
        }
      }catch(e) {
        console.error('handleMessage', e)
        reject(e)
      }
    });
  }
  
  browser.runtime.onMessage.addListener(handleMessage);
}

